//
//  sdl_pcm_display.c
//  FFmpegDemo
//
//  Created by xiaerfei on 2019/4/10.
//  Copyright © 2019 erfeixia. All rights reserved.
//

#include "sdl_pcm_display.h"
#include <SDL2/SDL.h>
static size_t BLOCK_SIZE = 4096000;
static int thread_exit=0;

static Uint8 *audio_buf = NULL;
static Uint8 *audio_pos = NULL;
static Uint32 audio_buffer_len = 0;

//event message
#define REFRESH_EVENT  (SDL_USEREVENT + 1)
#define QUIT_EVENT     (SDL_USEREVENT + 2)

static int refresh_video_timer(void *udata) {
    thread_exit=0;
    while (!thread_exit) {
        SDL_Event event;
        event.type = REFRESH_EVENT;
        SDL_PushEvent(&event);
        SDL_Delay(40);
    }
    thread_exit=0;
    
    //push quit event
    SDL_Event event;
    event.type = QUIT_EVENT;
    SDL_PushEvent(&event);
    
    return 0;
}
//callback function for audio devcie
void read_audio_data(void *udata, Uint8 *stream, int len){
    
    if(audio_buffer_len == 0){
        return;
    }
    
    SDL_memset(stream, 0, len);
    
    len = (len < audio_buffer_len) ? len : audio_buffer_len;
    SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
    
    audio_pos += len;
    audio_buffer_len -= len;
}

void sdl_pcm_display(void) {

    int height = 540;
    int width  = 960;
    const int video_width = 1920, video_height = 1080;
    Uint8 video_buf[BLOCK_SIZE];
    unsigned long video_buff_len = 0;
    unsigned long remain_len = 0;
    
    //SDL initialize
    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){
        fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
        return;
    }
    
    SDL_Window *window = SDL_CreateWindow("sdl diplay", 200, 200, width, height, SDL_WINDOW_SHOWN);
    
    if (window == NULL) {
        printf("创建 window 失败 ！！！");
        goto __exit;
    }
    
    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
    if (renderer == NULL) {
        printf("创建 renderer 失败 ！！！");
        goto __exit;
    }
    
    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, video_width, video_height);
    if (texture == NULL) {
        printf("创建 texture 失败 ！！！");
        goto __exit;
    }
    
    const char *audio_path = "/Users/xiaerfei/Documents/ffmpegDemo/res/cctv6live0.pcm";
    FILE *audio_fd = fopen(audio_path, "r");
    if(!audio_fd){
        fprintf(stderr, "Failed to open pcm file!\n");
        goto __exit;
    }
    
    //alloc memory for audio
    audio_buf = (Uint8*)malloc(BLOCK_SIZE);
    if(!audio_buf){
        goto __exit;
    }
    
    SDL_AudioSpec spec;
    spec.freq = 44100;;
    spec.format = AUDIO_S16SYS;
    spec.channels = 2;
    spec.silence = 0;
    spec.samples = 2048;;
    spec.callback = read_audio_data;;
    spec.userdata = NULL;
    
    //open audio devcie
    if(SDL_OpenAudio(&spec, NULL)){
        fprintf(stderr, "Failed to open audio device, %s\n", SDL_GetError());
        goto __exit;
    }
    audio_buffer_len = fread(audio_buf, 1, BLOCK_SIZE, audio_fd);
    fprintf(stderr, "block size is %zu\n", audio_buffer_len);
    audio_pos = audio_buf;
    
    const char *path = "/Users/xiaerfei/Documents/ffmpegDemo/res/cctv6live0.yuv";
    /*
     像素的采样位数一律为8bit。由于1Byte=8bit，所以一个像素的一个分量的采样值占用1Byte。
     
     98304
     256 * 256 = 65536
     128 * 128 = 16384
     128 * 128 = 16384
     
     从代码可以看出，如果视频帧的宽和高分别为w和h，那么一帧YUV420P像素数据一共占用w*h*3/2 Byte的数据。
     其中前w*h Byte存储Y，接着的w*h*1/4 Byte存储U，最后w*h*1/4 Byte存储V。
     */
    const unsigned int yuv_frame_len = video_width * video_height * 3 / 2;
    
    //open yuv file
    FILE *video_fd = fopen(path, "r");
    if( !video_fd ){
        fprintf(stderr, "Failed to open yuv file\n");
        goto __exit;
    }
    
    //read block data
    if((video_buff_len = fread(video_buf, (size_t)1, BLOCK_SIZE, video_fd)) <= 0){
        fprintf(stderr, "Failed to read data from yuv file!\n");
        goto __exit;
    }
    
    Uint8 *video_pos = video_buf;
    Uint8 *video_end = video_buf + video_buff_len;
    size_t blank_space_len = BLOCK_SIZE - video_buff_len;
    
    SDL_Thread *timer_thread = SDL_CreateThread(refresh_video_timer,
                                                NULL,
                                                NULL);
    SDL_Rect rect = {0,0,0,0};
    //play audio
    SDL_PauseAudio(0);
    do {
        SDL_Event event;
        SDL_WaitEvent(&event);
        if (event.type == REFRESH_EVENT) {
            if((video_pos + yuv_frame_len) > video_end) {
                //have remain data, but there isn't space
                remain_len = video_end - video_pos;
                if(remain_len && !blank_space_len) {
                    //copy data to header of buffer
                    memcpy(video_buf, video_pos, remain_len);
                    
                    blank_space_len = BLOCK_SIZE - remain_len;
                    video_pos = video_buf;
                    video_end = video_buf + remain_len;
                }
                
                //at the end of buffer, so rotate to header of buffer
                if(video_end == (video_buf + BLOCK_SIZE)){
                    video_pos = video_buf;
                    video_end = video_buf;
                    blank_space_len = BLOCK_SIZE;
                }
                
                //read data from yuv file to buffer
                if((video_buff_len = fread(video_end, 1, blank_space_len, video_fd)) <= 0){
                    fprintf(stderr, "eof, exit thread!");
                    thread_exit = 1;
                    continue;// to wait event for exiting
                }
                
                //reset video_end
                video_end += video_buff_len;
                blank_space_len -= video_buff_len;
                printf("not enought data: pos:%p, video_end:%p, blank_space_len:%zu\n", video_pos, video_end, blank_space_len);
                
//                if (audio_pos > (audio_buf + audio_buffer_len)) {
//                    audio_buffer_len = fread(audio_buf, 1, BLOCK_SIZE, audio_fd);
//                    fprintf(stderr, "block size is %zu\n", audio_buffer_len);
//
//                    audio_pos = audio_buf;
//                    if (audio_buffer_len == 0) {
//                        thread_exit = 1;
//                        break;
//                    }
//                }
                
            }
            SDL_UpdateTexture( texture, NULL, video_pos, video_width);
            //FIX: If window is resize
            rect.x = 0;
            rect.y = 0;
            rect.w = width;
            rect.h = height;
            SDL_RenderClear( renderer );
            SDL_RenderCopy( renderer, texture, NULL, &rect);
            SDL_RenderPresent( renderer );
            
            printf("not enought data: pos:%p, video_end:%p, blank_space_len:%zu\n", video_pos, video_end, blank_space_len);
            video_pos += yuv_frame_len;
        } else if (event.type == SDL_WINDOWEVENT) {
            // If Resize
            SDL_GetWindowSize(window, &width, &height);
        } else if (event.type == SDL_QUIT) {
            thread_exit = 1;
        } else if (event.type == QUIT_EVENT) {
            break;
        }
    } while (1);
    
    
    
__exit:
    if (renderer) {
        SDL_DestroyRenderer(renderer);
    }
    if (texture) {
        SDL_DestroyTexture(texture);
    }
    if (window) {
        SDL_DestroyWindow(window);
    }
    //close audio device
    SDL_CloseAudio();
    
    if (video_fd) {
        fclose(video_fd);
    }
    if (audio_fd) {
        fclose(audio_fd);
    }
    if (audio_buf) {
        free(audio_buf);
    }
    
    SDL_Quit();
}
